var express = require('express');
var exphbs = require('express-handlebars');
//var https = require('https');
var sha1 = require('sha1');
var fs = require('fs');
var rp = require('request-promise');

var debug = require('debug')('myapp:server');

var config = require('./config');
var tool = require('./tool');
var sign = require('./sign');
/*
var options = {
  key: fs.readFileSync(config.ssl.key),
  cert: fs.readFileSync(config.ssl.cert)
};
*/

//URL编码解码工具.
var urlencode = require('urlencode');

var cookieParser = require('cookie-parser')(config.cookieSecret);
var session = require('express-session')({
  secret: config.cookieSecret,
  cookie: {maxAge: 3600 * 1000},
  rolling: true
});
//var RedisStore = require('connect-redis')(session);
var bodyParser = require('body-parser');
var xmlParser = require('express-xml');

var app = express();

//app.use(cookieParser(config.cookieSecret));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser);
app.use(xmlParser);
app.use(session);
app.use(express.static(__dirname + '/source'));

app.engine('handlebars', exphbs({defaultLayout: 'main'}));
app.set('view engine', 'handlebars');

/**
 * 简单的更新 token 方法.
 * @param req 传递的 req
 * @param callback 更新成功后执行的回调函数.
 */
function updateToken(req, callback) {
  if ( req.session.token ) {
    callback.apply(this);
    return;
  }

  console.log('Token 过期了，正在重新获取...');

  rp({
    uri: config.path + 'token',
    json: true
  }).then(function(json) {
    console.log('Token 更新成功，最新值是：' + json['access_token']);
    req.session.token = json['access_token'];
    callback.apply(this);
  }, function(err) {
    console.log('Token 更新失败');
    console.log(err);
  });
};

/**
 * 验证消息真实性.
 * @param signature 微信加密签名，signature 结合了开发者填写的 token 参数和请求中的 timestamp 参数、nonce 参数。
 * @param timestamp 时间戳.
 * @param nonce 随机数.
 * @return (Boolean).
 */
function checkSignature(signature, timestamp, nonce) {
  var original = [nonce, timestamp, config.token].sort().join('');
  var scyptoString = sha1(original);
  return signature == scyptoString;
}

/*
app.use(session({
  store: new RedisStore(options),
  secret: config.secret
}));
*/
app.get('/', function(req, res) {
  //res.send("Hello World!");
  /*
  res.render('main', {
    pageTitle:"Say hi"
  });
  */

  res.send({
    "say": 'Hello'
  });

  /*
  res.render('home', {
    pageTitle:"Say hi"
  });
  */
});

/**
 * 验证消息的确来自微信服务器
 */
app.get('/signature', function(req, res) {

  var echostr = req.query.echostr;
  var signaturePass = checkSignature(
    req.query.signature,
    req.query.timestamp,
    req.query.nonce
  );

  res.send(signaturePass ? echostr : 'false');

});

/**
 * 微信消息被动回复.
 */
app.post('/signature', function(req, res) {

  //接收XML解析为JSON.
  var wxMessages = req.body.xml;
  var wxContent = '';

  //回复XML字符串.
  var message = '';
  //回复内容.
  var content = '';

  console.log("收到消息，内容如下：");
  console.log(wxMessages);

  //生成回复内容.
  switch (wxMessages.Content[0]) {
    case 'Hi':
    case 'Hello':
    case '你好':
      content = '我一直挺好的';
      break;
    case '嘿嘿':
    case '呵呵':
    case '哈哈':
      content = '有什么好笑的事情，说出来一起开心一下！';
      break;
    default:
      content = '我不懂你在说什么';
  }

  //拼装回复XML字符串.
  message = '<xml>'+
    '<ToUserName><![CDATA[' + wxMessages.FromUserName + ']]></ToUserName>' +
    '<FromUserName><![CDATA[' + wxMessages.ToUserName + ']]></FromUserName>' +
    '<CreateTime>' + (parseInt(new Date().getTime() / 1000) + '') + '</CreateTime>' +
    '<MsgType><![CDATA[text]]></MsgType>' +
    '<Content><![CDATA[' + content + ']]></Content>' +
  '</xml>';

  console.log('生成发送消息字符串：');
  console.log(message);

  res.set('Content-Type', 'text/xml');
  res.send(message);

});



app.get('/config', function(req, res) {
  //var url = req.query.url;

  var options = {
    uri: 'https://localhost:5000/token',
    headers: {
      'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate'
    },
    json: true
  };

  rp(options).then(function(json) {
    console.log(options);
    res.send(json);
  }).catch(function(err) {
    res.send(err);
  });
});

app.get('/baidu', function(req, res) {
  rp('https://baidu.com').then(function(htmlString) {
    console.log(htmlString.length);
    res.send(htmlString);
  }).catch(function(err) {
    res.send(err);
  });
});

/**
 * 获取 access token.
 * [官方文档](http://mp.weixin.qq.com/wiki/15/54ce45d8d30b6bf6758f68d2e95bc627.html)
 * @param grant_type 获取 access_token 填写 client_credential.
 * @param appid 第三方用户唯一凭证.
 * @param secret 第三方用户唯一凭证密钥，即 appsecret.
 * @return (JSON)
 *  succeed
 *    - access_token 获取到的凭证
 *    - expires_in 凭证有效时间，单位：秒
 *  error
 *    - errcode 错误代码
 *    - errmsg 错误消息
 */
app.get('/token', function(req, res) {

  var grantType = req.query.grant_type || 'client_credential';
  var options = {
    uri: config.url + 'token',
    qs: {
      grant_type: grantType,
      appid: config.appid,
      secret: config.secret,
    },
    headers: {
      'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate'
    },
    json: true
  };

  rp(options).then(function(json) {
    res.json(json);
  }, function(err) {
    res.json(err);
  });
});

/**
 * 获取微信服务器IP地址.
 * [官方文档](http://mp.weixin.qq.com/wiki/0/2ad4b6bfd29f30f71d39616c2a0fcedc.html)
 * @param access_token 公众号的access_token
 * @return (JSON)
 *  succeed
 *    - ip_list (Array) 微信服务器IP地址列表
 *  error
 *    - errcode 错误代码
 *    - errmsg 错误消息
 */
app.get('/getcallbackip', function(req, res) {

  function getIP() {
    rp({
      uri: config.url + 'getcallbackip',
      qs: {
        "access_token": req.session.token
      },
      json: true
    }).then(function(json) {
      res.json(json);
    }, function(err) {
      res.json(err);
    })
  }

  updateToken(req, getIP);

});

app.post('/say', function(req, res) {

  console.log('%s %s', req.method, req.url);
  console.log(req.body.xml);
	res.json(req.body);

});

/**
 * 微信授权响应处理，获取微信传递来的 code，用来换取网页授权 access_token.
 * @param code 授权码
 * @param state 授权形式
 */
app.get('/auth-response', function(req, res) {
  var code = req.query.code;
  var scope = req.query.state;

  console.log('获取 code：' + code);
  console.log('获取 scope：' + scope);

  rp({
    uri: 'https://api.weixin.qq.com/sns/oauth2/access_token?',
    qs: {
      appid: config.appid,
      secret: config.secret,
      code: code,
      grant_type: 'authorization_code'
    },
    json: true
  }).then(function(json) {

    //如果静默授权，则返回 openid 结束流程
    if (scope == 'snsapi_base') {
      console.log('静默授权流程结束，返回基础信息. 数据是：');
      console.log(json);
      //res.json(json);
      res.render('openid', {
        //layout: false,
        pageTitle:"用户信息",
        openid: json['openid']
      });
      return;
    }

    console.log('已获取 openid ，使用下面参数，开始获取用户信息');
    console.log({
      access_token: json['access_token'],
      openid: json['openid'],
      lang: 'zh_CN'
    });

    //否则继续请求，返回用户信息.
    return rp({
      uri: 'https://api.weixin.qq.com/sns/userinfo?',
      qs: {
        access_token: json['access_token'],
        openid: json['openid'],
        lang: 'zh_CN'
      },
      json: true
    });

  }, function(err) {

    console.log('使用 code 进行授权请求时出错:');
    console.log(err);
    res.json(err);

  }).then(function(json) {

    //静默授权已结束.
    if (scope == 'snsapi_base') {
      return;
    }

    if (!json) {
      console.log('获取用户信息失败');
      res.json({});
      return;
    }

    console.log('获取用户信息成功，用户信息如下：');
    console.log(json);

    //值为1时是男性，值为2时是女性，值为0时是未知
    var sexValues = ["未知", "男性", "女性"];

    res.render('user', {
      pageTitle:"用户信息",
      nickname: json['nickname'],
      sex: sexValues[json['sex']],
      province: json['province'],
      city: json['city'],
      country: json['country'],
      headimgurl: json['headimgurl']
    });


  }, function(err) {

    console.log('返回用户信息时出现错误');
    console.log(err);

    res.json(err);
  });

});

/**
 * 微信授权接口，分为静默和提示两种形式. 授权成功会重定向到新地址处理
 * @param snsapi (snsapi_base || snsapi_userinfo) 授权形式
 */
app.get('/authorize/:snsapi', function(req, res) {

  var callbackUrl = req.query.url || (config.path + 'auth-response');
  //默认使用静默授权.
  var snsapi = req.params.snsapi || 'snsapi_base';
  //对URL进行 UrlEncode 编码.
  callbackUrl = urlencode(callbackUrl);

  var url = 'https://open.weixin.qq.com/connect/oauth2/authorize?' +
    'appid=' + config.appid + '&' +
    'redirect_uri=' + callbackUrl + '&' +
    'response_type=' + 'code' + '&' +
    'scope=' + snsapi + '&' +
    'state=' + snsapi +
    '#wechat_redirect';

  console.log("---");
  console.log(url);

  res.redirect(url);
});

app.get('/show', function(req, res) {
  res.render('home', {
    pageTitle: "JS-SDK test"
  });
});

/**
 * 获取 access_token 换取 jsapi_ticket, jsapi_ticket是公众号用于调用微信JS接口的临时票据
 */
app.get('/getticket', function(req, res) {

  //var url = req.query.url || (config.path + 'getticket');
  //var type = req.query.url ? 'json' : 'html';
  var url = req.query.url;

  /**
   * 获取 Ticket.
   */
  function getticket() {
    rp({
      uri: 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?',
      qs: {
        'access_token': req.session.token,
        'type': 'jsapi'
      },
      json: true
    }).then(function(json) {
      console.log('获取 jsapi_ticket 成功');
      console.log(json);

      //res.json(json);

      console.log('test ed', url);

      //生成签名数据，并且连同 signature 一同返回.
      var signJSON = sign(json['ticket'], url);

      console.log('生成 signJSON 成功');
      console.log(signJSON);

      //如果签名数据存在，则追加属性.
      if (signJSON) {
        signJSON['appId'] = config.appid;
        console.log('丰富 signJSON 成功');
        console.log(signJSON);
      }

      //返回给前端
      //res.json(signJSON);

      //返回给前端
      res.json({
        appId: signJSON['appId'],
        timestamp: signJSON['timestamp'],
        nonceStr: signJSON['nonceStr'],
        signature: signJSON['signature']
      });

      /*
      res.render('home', {
        pageTitle: "JS-SDK test",
        appId: signJSON['appId'],
        timestamp: signJSON['timestamp'],
        nonceStr: signJSON['nonceStr'],
        signature: signJSON['signature']
      });
      */

    }, function(err) {
      console.log('获取 jsapi_ticket 失败');
      console.log(err);
    });
  }

  //var originUrl = req.path;
  //如果 token 未过期，则直接拿去生成凭证数据.
  if(req.session.token) {
    getticket();
    return;
  }

  //否则先刷新 token，然后再生成凭证数据.
  updateToken(req, getticket);
});

app.set('port', config.port);

/*
https.createServer(options, app).listen(app.get('port'), function() {
  console.log('Example app listening on port ' + app.get('port') + '!');
});
*/

app.on('error', function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
});
app.on('listening', function onListening() {
  var addr = app.address();
  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
});

app.listen(app.get('port'), function() {
  //console.log(config);
  console.log('Example app listening on port ' + app.get('port') + '!');
});
